package asteroids;

import be.kuleuven.cs.som.annotate.*;

/**
 * This class represents vectors composed out of two coordinates. 
 *
 * @author Thijs Dieltjens & Wim Konings
 * @version 2.0
 *
 * @invar	 The coordinates of this vector are valid.
 * 			|isValid(getXCoordinate) && isValid(getYCoordinate)
 */
@Value
public class Vector {

	/**
	 * Initializes a vector with given coordinates.
	 * 
	 * @param	xCoordinate
	 * 			given xCoordinate for this vector.
	 * @param 	yCoordinate
	 * 			given yCoordinate for this vector.
	 * @throws IllegalArgumentException
	 * 			One or both of the coordinates isn't valid.
	 * 			|!isValidCoordinate(xCoordinate) || !isValidCoordinate(yCoordinate)
	 */
	public Vector(double xCoordinate,double yCoordinate) throws IllegalArgumentException{
		if(isValidCoordinate(xCoordinate) && isValidCoordinate(yCoordinate)){
			this.xCoordinate=xCoordinate;
			this.yCoordinate=yCoordinate;
		}
		else{
			throw new IllegalArgumentException();
		}
		
	}

	/**
	 * returns the x-Coordinate of this vector
	 */
	@Basic @Immutable
	public Double getXCoordinate(){
		return xCoordinate;
	}
	
	/**
	 * returns the y-Coordinate of this vector
	 */
	@Basic @Immutable
	public Double getYCoordinate(){
		return yCoordinate;
	}

	/**
	 * This methods returns the sum of two vectors
	 * @param vector
	 * 			Vector to be added to this vector.
	 * @return	Difference of the two vectors
	 * 			|new Vector(vector.getXCoordinate()-this.getXCoordinate(),vector.getYCoordinate()-this.getYCoordinate())
	 * @throws NullPointerException
	 * 			Given vector is null
	 * 			| vector==null
	 * @throws IllegalStateException
	 * 			One of the calculated vectors isn't valid.
	 * 			|!isValidCoordinate(vector.getXCoordinate()-this.getXCoordinate())
	 *			|	|| isValidCoordinate(vector.getYCoordinate()-this.getYCoordinate())
	 */
	@Immutable
	public Vector subtractVector(Vector vector){
		if(vector==null){
			throw new NullPointerException();
		}
		if(!isValidCoordinate(-vector.getXCoordinate()+this.getXCoordinate())
				|| !isValidCoordinate(-vector.getYCoordinate()+this.getYCoordinate())){
			throw new IllegalStateException();
		}
		Vector result = new Vector(-vector.getXCoordinate()+this.getXCoordinate(),-vector.getYCoordinate()+this.getYCoordinate());
		return result;

	}

	
	/**
	 * This method calculates the dotproduct of this Vector and the given vector.
	 * @param	vector
	 * 			The vector from which the dotproduct needs to be calculated.
	 * @return	The dotProduct of this vector and the given vector
	 * 			| this.getXCoordinate()*vector.getXCoordinate() + this.getYCoordinate()*vector.getYCoordinate()
	 * @throws IllegalStateException
	 * 			Result is infinite.
	 * 			|Math.abs(this.getXCoordinate()*vector.getXCoordinate() + this.getYCoordinate()*vector.getYCoordinate()) > Double.MAX_VALUE
	 * 			
	 */
	public Double dotProduct(Vector vector) throws IllegalStateException{
		Double xProduct = this.getXCoordinate()*vector.getXCoordinate();
		Double yProduct = this.getYCoordinate()*vector.getYCoordinate();
		Double result = xProduct+yProduct;
		if(result.isInfinite()){
			throw new IllegalStateException();
		}
		else{
			return result;
		}
	}

	/**
	 * Checks if the given x or y coordinate of this ship is valid. 
	 * 
	 * @param	coordinate
	 * 			The given coordinate in the x or y direction of this ship.
	 * @return 	False if and only if the coordinate is not a valid number or infinity. 
	 * 			| if((coordinate.isNaN()) || (coordinate.isInfinite()))
	 * 			| 	then result == false
	 * 			| else result == true
	 */
	public static boolean isValidCoordinate(Double coordinate) {
		if((coordinate.isNaN()) || coordinate.isInfinite()) {
			return false;
		}
		else {
			return true;
		}
	}

	private final double xCoordinate;
	private final double yCoordinate;
	
	/**
	 * Returns a textual representation of this vector.
	 * 
	 *  @return A string consisting of the textual representation of a vector.
	 *  		|result.equals("("+getXCoordinate+","+getYCoordinate+")")
	 * 
	 */
	@Override
	public String toString(){
		return "("+xCoordinate+","+yCoordinate+")";
	}
	
	
	/**
	 * Return the hashcode of this vector.
	 */
	@Override
	public int hashCode(){
		return (getXCoordinate().hashCode() + getYCoordinate().hashCode());
	}
	 /** This method calculates the distance between two vectors.
	 * @param 	vector
	 * 			The vector between to which the distance is measured.
	 * @param 	spaceObject
	 * 			The space object you want to compare distance with.
	 * @return	The mathematical distance between the two ships.
	 * 			| result == Math.sqrt( (this.getXCoordinate()-vector.getXCoordinate())^2 
	 * 			|					+ (this.getYCoordinate()-vector.getYCoordinate())^2)
	 * @throws	NullPointerException
	 * 			| (vector==null)
	 * @throws	IllegalStateException
	 * 			The difference in x- or y-coordinates becomes infinity.
	 * 		  	| Math.abs(this.getCoordinates().getXCoordinate()-spaceObject.getCoordinates().getXCoordinate())>Double.MAX_VALUE 
	 * 			|	|| Math.abs(this.getCoordinates().getYCoordinate()-spaceObject.getCoordinates().getYCoordinate())>Double.MAX_VALUE
	 * @throws	IllegalStateException
	 * 			The powers become infinity or the sum of the two powers becomes infinity.
	 * 			| Math.abs((this.getCoordinates().getXCoordinate()-spaceObject.getCoordinates().getXCoordinate())^2)>Double.MAX_VALUE 
	 * 			|	|| Math.abs((this.getCoordinates().getYCoordinate()-spaceObject.getCoordinates().getYCoordinate())^2) > Double.MAX_VALUE
	 * 			|	|| Math.abs(this.getCoordinates().getXCoordinate()-spaceObject.getCoordinates().getXCoordinate())^2 
	 * 			|				+ (this.getCoordinates().getYCoordinate()-spaceObject.getCoordinates().getYCoordinate())^2) > Double.MAX_VALUE
	 *
	 **/
	public double distanceTo(Vector vector) throws NullPointerException, IllegalStateException{
		if(vector==null){
			throw new NullPointerException();
		}
		else{
			Double xCo1=this.getXCoordinate();
			Double xCo2=vector.getXCoordinate();
			Double yCo1=this.getYCoordinate();
			Double yCo2=vector.getYCoordinate();

			Double diffX = xCo1-xCo2;
			Double diffY = yCo1-yCo2;
			if(diffX.isInfinite()  || diffY.isInfinite()){
				throw new IllegalStateException();
			}
			else{
				Double powerX = Math.pow(diffX, 2);
				Double powerY = Math.pow(diffY, 2);
				Double sumOfPowers = powerX+powerY;
				if(powerX.isInfinite() || powerY.isInfinite() || sumOfPowers.isInfinite()){
					throw new IllegalStateException();
				}
				else{
					Double result = Math.sqrt(sumOfPowers);
					if(result.isInfinite()){
						throw new IllegalStateException();
					}
					else{
						return result;
					}
				}
			}
		}
	}
	
	
	/**
	 * @param vector
	 * 			The Vector to compare with.
	 * @return	True if the xCoordinates of both vectors are equal and the yCoordinates of both vectors are too.
	 * 			|result = (Util.fuzzyEquals(vector.getXCoordinate(),getXCoordinate()) &&
	 *			|Util.fuzzyEquals(vector.getYCoordinate(),getYCoordinate()))
	 */
	public boolean equals(Vector vector){
		return (Util.fuzzyEquals(vector.getXCoordinate(),getXCoordinate()) &&
				Util.fuzzyEquals(vector.getYCoordinate(),getYCoordinate()));			
		
	}
	
}